/*
 * @Description:
 * @Author: TzzzT
 * @Date: 2022-04-05 15:51:56
 * @LastEditTime: 2023-05-04 23:54:45
 * @LastEditors: TzzzT
 */

#include "nvs.h"
#include "nvs_flash.h"
#include "esp_log.h"
#include "string.h"
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"

#include "esp_timer.h"

#include "esp_adc/adc_oneshot.h"
#include "esp_adc/adc_continuous.h"
#include "esp_adc/adc_cali.h"


#include "esp_system.h"
#include "esp_mac.h"
#include "RCS_protocol.h"
#include "RCS_master.h"
#include "RCS_ble_master.h"
#include "myble.h"
#include "cJSON.h"
#include "RCS_net.h"
#include "bc260.h"

static const char *SYS_TAG = "SYS_TAG";
static const char *SHOCK_TAG = "SHOCK_TAG";
static const char *BATT_TAG = "BATT_TAG";
static const char *GAME_TAG = "GAME_TAG";

static SemaphoreHandle_t xSystemMutex = NULL;

rcs_mast_system_t rcsSystem = {
    .game.player.bullet_nums = 999,
    .game.player.damage = 30,
    .game.player.hp = 100,
    .game.player.id = 0,
    .game.player.living = true,
    .game.player.room = 0,
    .game.player.team = 0,
    .game.player.kill = 0,
    .game.player.death = 0,
    .game.player.is_in_invicible = false,
    .game.player.team_score = 0,
    .game.player.death_player = {0},
    .game.start = false,

    .game.player.team_id = 0,
    .game.player.room_id = 0,
    .game.record_id = 0,

    .net.first_connect = true,
    .net.is_connected = false,
    .net.start_heartbeat = false,

    .equip.pri_weapon.point.batt = -1,
    .equip.pri_weapon.point.is_hold = false,
    .equip.pri_weapon.screen.batt = -1,
    .equip.pri_weapon.screen.is_hold = false,
    .equip.armor.batt = -1,
    .equip.armor.is_hold = false,
};

extern TaskHandle_t shockTaskHandle;

/////////////////////SYSTEM/////////////////////

#if 0
#define IFNET_KEY "ifnet_key"
esp_err_t store_ifnet_to_nvs(void)
{
    nvs_handle devNvsHandle;
    esp_err_t err;
    err = nvs_open("dev_config", NVS_READWRITE, &devNvsHandle);
    if (err != ESP_OK)
    {
        printf("Error (%s) opening NVS devNvsHandle!\n", esp_err_to_name(err));
        return err;
    }
    nvs_set_u8(devNvsHandle, IFNET_KEY, rcsSystem.net.is_connected);
    err = nvs_commit(devNvsHandle);
    if (err != ESP_OK)
    {
        printf("store_ifnet_to_nvs failed, err(%s)\n", esp_err_to_name(err));
        return err;
    }
    nvs_close(devNvsHandle);
    return ESP_OK;
}

esp_err_t get_ifnet_from_nvs(void)
{
    nvs_handle devNvsHandle;
    esp_err_t err;
    err = nvs_open("dev_config", NVS_READWRITE, &devNvsHandle);
    if (err != ESP_OK)
    {
        printf("Error (%s) opening NVS devNvsHandle!\n", esp_err_to_name(err));
        return err;
    }
    uint8_t val = 0;
    err = nvs_get_u8(devNvsHandle, IFNET_KEY, &val);
    if (err != ESP_OK)
    {
        printf("get_ifnet_from_nvs failed, err(%s)\n", esp_err_to_name(err));
        return err;
    }
    else
    {
        // ESP_LOGI(SYS_TAG, "get_ifnet_from_nvs successfully, state: %d",val);
    }
    rcsSystem.net.is_connected = val;
    nvs_commit(devNvsHandle);
    nvs_close(devNvsHandle);
    return ESP_OK;
}




#define GAME_START_KEY "game_start_key"
esp_err_t store_game_start_to_nvs(void)
{
    nvs_handle devNvsHandle;
    esp_err_t err;
    err = nvs_open("dev_config", NVS_READWRITE, &devNvsHandle);
    if (err != ESP_OK)
    {
        printf("Error (%s) opening NVS devNvsHandle!\n", esp_err_to_name(err));
        return err;
    }
    nvs_set_u8(devNvsHandle, GAME_START_KEY, rcsSystem.game.start);
    err = nvs_commit(devNvsHandle);
    if (err != ESP_OK)
    {
        printf("store_game_start_to_nvs failed, err(%s)\n", esp_err_to_name(err));
        return err;
    }
    nvs_close(devNvsHandle);
    return ESP_OK;
}

esp_err_t get_game_start_from_nvs(void)
{
    nvs_handle devNvsHandle;
    esp_err_t err;
    err = nvs_open("dev_config", NVS_READWRITE, &devNvsHandle);
    if (err != ESP_OK)
    {
        printf("Error (%s) opening NVS devNvsHandle!\n", esp_err_to_name(err));
        return err;
    }
    uint8_t val = 0;
    err = nvs_get_u8(devNvsHandle, GAME_START_KEY, &val);
    if (err != ESP_OK)
    {
        printf("get_game_start_from_nvs failed, err(%s)\n", esp_err_to_name(err));
        return err;
    }
    else
    {
        // ESP_LOGI(SYS_TAG, "get_game_start_from_nvs successfully, state: %d",val);
    }
    rcsSystem.game.start = val;
    nvs_commit(devNvsHandle);
    nvs_close(devNvsHandle);
    return ESP_OK;
}

#endif

static void systemRecursiveMutexCreate(void)
{
    /* 创建互斥信号量 */
    xSystemMutex = xSemaphoreCreateRecursiveMutex();
    if (xSystemMutex == NULL)
    {
        ESP_LOGI(SYS_TAG, "systemmutexCreate failed");
    }
}

void take_mutex(void)
{
    // ESP_LOGI(SYS_TAG, "wait_mutex");
    if (xSystemMutex != NULL)
    {
        xSemaphoreTakeRecursive(xSystemMutex, portMAX_DELAY);
    }
    // ESP_LOGI(SYS_TAG, "get_mutex");
}

void give_mutex(void)
{
    if (xSystemMutex != NULL)
    {
        xSemaphoreGiveRecursive(xSystemMutex);
    }
    // ESP_LOGI(SYS_TAG, "free_mutex");
}

static void get_chip_default_mac(void)
{
    // esp_base_mac_addr_get(mac); 得到0
    // ESP_LOGE(SYS_TAG, "esp_base_mac_addr_get:");
    // esp_log_buffer_hex(SYS_TAG, mac, 8);
    // esp_efuse_mac_get_custom(mac); 得到0
    // ESP_LOGE(SYS_TAG, "esp_efuse_mac_get_custom:");
    // esp_log_buffer_hex(SYS_TAG, mac, 8);
    esp_efuse_mac_get_default(rcsSystem.chip_mac);
    ESP_LOGE(SYS_TAG, "esp_efuse_mac_get_default:");
    esp_log_buffer_hex(SYS_TAG, rcsSystem.chip_mac, 8);

    esp_read_mac(rcsSystem.equip.core.cli_bda, ESP_MAC_BT);
    ESP_LOGE(SYS_TAG, "esp_efuse_mac_ble:");
    esp_log_buffer_hex(SYS_TAG, rcsSystem.equip.core.cli_bda, 6);

    // esp_read_mac(rcsSystem.device.mac_addr, ESP_MAC_IEEE802154);
    // ESP_LOGE(SYS_TAG, "esp_read_mac ESP_MAC_IEEE802154:");
    // esp_log_buffer_hex(SYS_TAG, rcsSystem.device.mac_addr, 8);
}

// 暂时从芯片mac地址数组转化成十进制数取后六位，有导致重复的风险
void get_user_id(void)
{
    uint32_t chip_id = 0;
    get_chip_default_mac();

    chip_id |= rcsSystem.chip_mac[3] << 16;
    chip_id |= rcsSystem.chip_mac[4] << 8;
    chip_id |= rcsSystem.chip_mac[5];

    chip_id = chip_id % 1000000;
    ESP_LOGI(SYS_TAG, "system id: %06" PRIu32 "", chip_id);
    // itoa(chip_id, rcsSystem.user.dno, 10);
    // esp_log_buffer_char(SYS_TAG, rcsSystem.user.dno, 6);
    rcsSystem.user.id = chip_id;
    return;
}

/**
 * @description: 上电后调用，恢复设备信息，重连旧设备
 * @param  {void}
 * @return {void}
 * @Author: TzzzT
 * @Date: 2022-04-19 23:39:26
 * @LastEditTime: Do not edit
 * @LastEditors: Shadow
 */

void remember_sys_state(void)
{
    systemRecursiveMutexCreate();
    // system_time_init();
    // 读取设备状态
    // ESP_LOGE(SYS_TAG, "state: %s", state_name(rcsSystem.device.state));
    // 目前初始化的时候user的id由芯片的mac地址转化而来
    get_user_id();

    // get_game_start_from_nvs();
}

static SemaphoreHandle_t eventMutex = NULL;

rcs_event_t game_event = {
    .class = NO_EVENT,
    .count = 0,
};

void game_event_init(void)
{
    eventMutex = xSemaphoreCreateMutex();
    if (eventMutex == NULL)
    {
        ESP_LOGI(GAME_TAG, "eventMutex Create failed");
    }
}

void set_game_event(rcs_event_class_t _class, uint8_t _count)
{
    xSemaphoreTake(eventMutex, portMAX_DELAY);
    game_event.class = _class;
    game_event.count = _count;
    xSemaphoreGive(eventMutex);
}

void update_game_event(void)
{
    xSemaphoreTake(eventMutex, portMAX_DELAY);
    if (game_event.count <= 1)
    {
        game_event.count = 0;
        game_event.class = NO_EVENT;
    }
    else
    {
        game_event.count -= 1;
    }
    xSemaphoreGive(eventMutex);
}

rcs_event_t *get_game_event_state(void)
{
    return &game_event;
}

//////////////////////////BATTERY//////////////////////////
bool update_low_battery_alert_flag(void)
{
    bool now = rcsSystem.equip.low_batt_alert;

    if (rcsSystem.equip.core.battery < LOW_BATTERY)
    {
        rcsSystem.equip.low_batt_alert = true;
    }

    if (!rcsSystem.equip.pri_weapon.point.is_hold)
    {
        rcsSystem.equip.pri_weapon.point.batt = -1;
    }
    else if (rcsSystem.equip.pri_weapon.point.batt < LOW_BATTERY)
    {
        rcsSystem.equip.low_batt_alert = true;
    }

    if (!rcsSystem.equip.pri_weapon.screen.is_hold)
    {
        rcsSystem.equip.pri_weapon.screen.batt = -1;
    }
    else if (rcsSystem.equip.pri_weapon.screen.batt < LOW_BATTERY)
    {
        rcsSystem.equip.low_batt_alert = true;
    }

    if (!rcsSystem.equip.armor.is_hold)
    {
        rcsSystem.equip.armor.batt = -1;
    }
    else if (rcsSystem.equip.armor.batt < LOW_BATTERY)
    {
        rcsSystem.equip.low_batt_alert = true;
    }

    if (now != rcsSystem.equip.low_batt_alert)
    {
        return true;
    }
    else
    {
        return false;
    }
}

//////////////////////////SHOCK//////////////////////////

void shock_init(void)
{
    ESP_LOGI(SHOCK_TAG, "shock_init");
    gpio_reset_pin(SHOCK_OUTPUT_PIN);
    /* Set the GPIO as a push/pull output */
    gpio_set_direction(SHOCK_OUTPUT_PIN, GPIO_MODE_OUTPUT);
}
void set_shock(bool state)
{
    /* Set the GPIO level according to the state (LOW or HIGH)*/
    gpio_set_level(SHOCK_OUTPUT_PIN, state);
}

//////////////////////////POWER//////////////////////////

/**
 * @brief: 在按钮初始化以后调用此函数，先是手动保持按钮按下来供电，保持2s，同时正在执行delay_ms。
 *          2s，以后，在此程序种将引脚拉高，保证供电，可以松开按钮
 * @param  {*}
 * @return {*}
 * @Author: TzzzT
 * @Date: 2022-05-11 19:25:21
 * @LastEditTime: Do not edit
 * @LastEditors: Shadow
 */
void power_init(void)
{
    gpio_reset_pin(POWER_PIN);
    gpio_set_direction(POWER_PIN, GPIO_MODE_INPUT_OUTPUT);
    gpio_set_level(POWER_PIN, true);
    vTaskDelay(pdMS_TO_TICKS(1000));
    ESP_LOGI(SYS_TAG, "end power init");
}
void power_off(void)
{
    gpio_set_level(POWER_PIN, false);
}

//////////////////////////DELAY//////////////////////////
bool delay_flag = false;
static uint16_t _x = 0;
static void delay_timer_callback(void *arg)
{
    int64_t time_since_boot = esp_timer_get_time();
    delay_flag = true;
    ESP_LOGI(SYS_TAG, "One-shot timer called, time since boot: %lld us, set flag: %d", time_since_boot, delay_flag);
}
void delay_ms(uint16_t ms)
{
    const esp_timer_create_args_t oneshot_timer_args = {
        .callback = &delay_timer_callback,
        .name = "one-shot",
    };
    esp_timer_handle_t oneshot_timer;
    ESP_ERROR_CHECK(esp_timer_create(&oneshot_timer_args, &oneshot_timer));
    ESP_ERROR_CHECK(esp_timer_start_once(oneshot_timer, ms * 1000));
    while (!delay_flag)
    {
        ESP_LOGI(SYS_TAG, "delaying.");
        _x++;
    }
    ESP_LOGI(SYS_TAG, "delay %d ms finish.", ms);
    ESP_ERROR_CHECK(esp_timer_delete(oneshot_timer));
}

//////////////////////////GAME//////////////////////////

bool change_player_hp(player_hp_t new_hp)
{
    bool change = false;
    if (new_hp < 0)
    {
        new_hp = 0;
    }
    else if (new_hp > 9999)
    {
        new_hp = 9999;
    }
    rcsSystem.game.player.living = (new_hp > 0) ? true : false;

    if (rcsSystem.game.player.is_in_invicible && new_hp < rcsSystem.game.player.hp)
    {
        ESP_LOGI("hp", "invincible. hp: %d", rcsSystem.game.player.hp);
        return change;
    }

    change = (rcsSystem.game.player.hp != new_hp) ? true : false;
    rcsSystem.game.player.hp = new_hp;
    // ESP_LOGI("hp", "new hp: %d", rcsSystem.game.player.hp);
    // 如果是直接被设置成死亡了，也要唤醒shock任务。
    // 如果不是本设备被击中导致的扣血，那么不唤醒shock任务，这样可以区分伤害来源。
    if (!rcsSystem.game.player.living)
    {
        eTaskState state = eTaskGetState(shockTaskHandle);
        if (state == eSuspended)
        {
            // ESP_LOGE(SYS_TAG,"vTaskResume( shockTaskHandle )");
            vTaskResume(shockTaskHandle);
        }
    }
    return change;
}

bool change_player_damage(player_damage_t new_damage)
{
    if (new_damage < 0)
    {
        new_damage = 0;
    }
    else if (new_damage > 2550)
    {
        new_damage = 2550;
    }
    bool change = (rcsSystem.game.player.damage != new_damage) ? true : false;
    rcsSystem.game.player.damage = new_damage;
    return change;
}

bool change_player_kill(player_kill_t kill)
{
    bool change = (rcsSystem.game.player.kill != kill) ? true : false;
    rcsSystem.game.player.kill = kill;
    return change;
}

bool change_player_death(player_death_t death)
{
    bool change = (rcsSystem.game.player.death != death) ? true : false;
    rcsSystem.game.player.death = death;
    return change;
}

bool change_player_team_score(player_team_score_t team_score)
{
    bool change = (rcsSystem.game.player.team_score != team_score) ? true : false;
    rcsSystem.game.player.team_score = team_score;
    return change;
}

bool change_player_bullet_nums(bullet_num_t new_bullet_num)
{
    if (new_bullet_num < 0)
    {
        new_bullet_num = 0;
    }
    else if (new_bullet_num > 999)
    {
        new_bullet_num = 999;
    }
    bool change = (rcsSystem.game.player.bullet_nums != new_bullet_num) ? true : false;
    rcsSystem.game.player.bullet_nums = new_bullet_num;
    return change;
}

bool change_player_team(uint8_t new)
{
    if (new > 9)
    {
        new = 9;
    }
    bool change = (rcsSystem.game.player.team != new) ? true : false;
    rcsSystem.game.player.team = new;
    return change;
}

bool change_player_room(uint8_t new)
{
    if (new > 9)
    {
        new = 9;
    }
    bool change = (rcsSystem.game.player.room != new) ? true : false;
    rcsSystem.game.player.room = new;
    return change;
}

bool change_game_state(bool new_state)
{
    bool change = (rcsSystem.game.start != new_state) ? true : false;
    rcsSystem.game.start = new_state;
    if (new_state)
    {
        rcsSystem.game.have_net_in_start = rcsSystem.net.is_connected;
    }
    else
    {
        rcsSystem.game.have_net_in_start = false;
    }
    return change;
}

bool reset_player(void)
{
    bool ret = false;
    ret |= change_player_hp(100);
    ret |= change_player_damage(30);
    ret |= change_player_team(0);
    ret |= change_player_room(0);
    ret |= change_player_team_score(0);
    ret |= change_player_death(0);
    ret |= change_player_kill(0);
    ret |= change_player_bullet_nums(999);
    // ESP_LOGI(GAME_TAG, "reset player.\nhp: %d, damage: %d. team: %d. room: %d. team score: %d. death: %d. kill: %d. bullet: %d.",
    // rcsSystem.game.player.hp, rcsSystem.game.player.damage, rcsSystem.game.player.team,
    // rcsSystem.game.player.room, rcsSystem.game.player.team_score, rcsSystem.game.player.death,
    // rcsSystem.game.player.kill, rcsSystem.game.player.bullet_nums);
    return ret;
}

void reset_game_setting(void)
{
    reset_player();
    change_game_state(false);
}

void game_setting_init(void)
{
    // game_timer_init();
    reset_game_setting();
    rcsSystem.game.player.id = rcsSystem.user.id;
}

//////////////////////////消息处理函数//////////////////////////

uint8_t *make_prop_command_package(uint8_t command, uint8_t batt)
{
    uint8_t package_len = PACKAGE_LEN_UPL_LEN + MESSAGE_TYPE_BATTERY_UPL_LEN + COMMAND_UPL_LEN;
    uint8_t *package = calloc(1, package_len * sizeof(uint8_t));
    uint16_t data_head = 0;
    uint8_t data_len;

    data_len = PACKAGE_LEN_UPL_LEN;
    package[data_head] = data_len;
    package[data_head + 1] = PACKAGE_LEN_UPL;
    package[data_head + 2] = package_len;
    data_head += data_len;

    data_len = MESSAGE_TYPE_BATTERY_UPL_LEN;
    package[data_head] = data_len;
    package[data_head + 1] = MESSAGE_TYPE_BATTERY_UPL;
    package[data_head + 2] = CORE_ARMOR_COMMAND_TYPE;
    package[data_head + 3] = batt;
    data_head += data_len;

    data_len = COMMAND_UPL_LEN;
    package[data_head] = data_len;
    package[data_head + 1] = COMMAND_UPL;
    package[data_head + 2] = command;
    data_head += data_len;
    return package;
}

/*
开火事件：
发射板上传剩余子弹和电量。
主机通知手机，
子弹数量变了通知从机(发射板)
*/
static void fire_message_handle(uint8_t *buf, uint16_t buf_len)
{
    ESP_LOGI(GAME_TAG, "fire handle");
    uint16_t data_head = 0;
    upload_data_type_t data_type;
    uint16_t data_len;
    while (data_head < buf_len)
    {
        data_len = buf[data_head];
        data_type = buf[data_head + 1];
        switch (data_type)
        {
        case REMAIN_BULLET_NUM_UPL:
            rcsSystem.game.player.bullet_nums = 0;
            rcsSystem.game.player.bullet_nums |= buf[data_head + 2] << 8;
            rcsSystem.game.player.bullet_nums |= buf[data_head + 3];
            break;
        default:
            break;
        }
        data_head += data_len;
    }

    // 通知服务器
    size_t message_length;
    char *message = make_net_json_package(USE_BULLET_NET_UP, &message_length, NULL);
    if (message)
    {
        bc_send_package_to_server(message, message_length);
        free(message);
    }

    // 同步武器从机属性
    uint16_t packlen;
    uint8_t *sync_ble_server_data = make_sync_data_package(&packlen, (1 << BULLET_NUM_NOTIFY));
    if (rcsSystem.equip.pri_weapon.screen.is_hold)
    {
        // ESP_LOGI(GAME_TAG, "screen.is_hold, update bullet num:%d", rcsSystem.game.player.bullet_nums);
        rcs_send_to_bleserver(GUN_SCREEN_CLASS, sync_ble_server_data, packlen);
    }
    if (rcsSystem.equip.pri_weapon.point.is_hold)
    {
        // ESP_LOGI(GAME_TAG, "point.is_hold, update bullet num:%d", rcsSystem.game.player.bullet_nums);
        rcs_send_to_bleserver(GUN_POINT_CLASS, sync_ble_server_data, packlen);
    }
    free(sync_ble_server_data);
}

/*
接收板不核对数据内容，直接传到主机。
首先解析数据内容。
低电量改为另外部分检测是否通知
然后判断是否有效命中、死亡。
有效命中：通知手机。
掉血->通知从机，通知手机。
*/
static void be_hitted_message_handle(uint8_t *buf, uint16_t buf_len)
{
    player_t enemy = {0};
    uint16_t data_head = 0;
    upload_data_type_t data_type;
    uint16_t data_len;
    while (data_head < buf_len)
    {
        data_len = buf[data_head];
        data_type = buf[data_head + 1];
        switch (data_type)
        {
        case ROOM_UPL:
            enemy.room = buf[data_head + 2];
            // ESP_LOGE(GAME_TAG, "enemy.room:%d", enemy.room);
            break;
        case TEAM_UPL:
            enemy.team = buf[data_head + 2];
            // ESP_LOGE(GAME_TAG, "enemy.team:%d", enemy.team);
            break;
        case DAMAGE_UPL:
            enemy.damage = 0;
            enemy.damage |= buf[data_head + 2] << 8;
            enemy.damage |= buf[data_head + 3];
            // ESP_LOGE(GAME_TAG, "enemy damage:%d", enemy.damage);
            break;
        case PLAYER_ID_UPL:
            enemy.id = 0;
            enemy.id |= buf[data_head + 2] << 16;
            enemy.id |= buf[data_head + 3] << 8;
            enemy.id |= buf[data_head + 4];
            // ESP_LOGE(GAME_TAG, "enemy id:%6d", enemy.id);
            break;
        default:
            break;
        }
        data_head += data_len;
    }
    /*
    扣血条件:
    1.开始游戏
    2.活着
    3.0. 测试模式：自己的队伍和房间都为0
    3.1. 个人竞技模式：房间相同，队伍为0.
    3.2. 团队竞技模式：房间相同，队伍不同。
    */
    uint8_t valid_flag = 0;
    if (!rcsSystem.game.start || !rcsSystem.game.player.living)
    {
        return;
    }
    else if (rcsSystem.game.player.room == 0 && rcsSystem.game.player.team == 0)
    {
        // 3.0. 测试模式：自己的队伍和房间都为0
        ESP_LOGI(GAME_TAG, "Test mode bullet");
        valid_flag = 1;
    }
    else if (enemy.room != 0 && enemy.room == rcsSystem.game.player.room &&
             enemy.team == 0 && rcsSystem.game.player.team == 0)
    {
        // 3.1. 个人竞技模式：房间相同，队伍为0.
        ESP_LOGI(GAME_TAG, "Individual competition mode bullet");
        valid_flag = 1;
    }
    else if (enemy.room != 0 && enemy.room == rcsSystem.game.player.room &&
             enemy.team != 0 && rcsSystem.game.player.team != 0 && enemy.team != rcsSystem.game.player.team)
    {
        // 3.2. 团队竞技模式：房间相同，队伍不同。
        ESP_LOGI(GAME_TAG, "Team competition mode bullet");
        valid_flag = 1;
    }

    if (valid_flag)
    {

        change_player_hp(rcsSystem.game.player.hp - enemy.damage);
        ESP_LOGE(GAME_TAG, "be hit");

        // 通知服务器
        uint32_t param_arr[2] = {0};
        param_arr[0] = enemy.id;
        param_arr[1] = enemy.damage;
        size_t message_length;
        // ESP_LOGI("net", "send be hitted message to server");
        char *message = make_net_json_package(BE_HITTED_NET_UP, &message_length, param_arr);
        if (message)
        {
            bc_send_package_to_server(message, message_length);
            free(message);
        }

        // 通知所有从机，新的血量已经玩家存活状态
        uint16_t packlen;
        notify_mask_t notify_data_mask = (1 << HP_NOTIFY) | (1 << LIVING_NOTIFY | (1 << BE_HITTED_NOTIFY));
        uint8_t *sync_ble_server_data = make_sync_data_package(&packlen, notify_data_mask);
        if (rcsSystem.equip.pri_weapon.screen.is_hold)
        {
            rcs_send_to_bleserver(GUN_SCREEN_CLASS, sync_ble_server_data, packlen);
        }
        if (rcsSystem.equip.pri_weapon.point.is_hold)
        {
            rcs_send_to_bleserver(GUN_POINT_CLASS, sync_ble_server_data, packlen);
        }
        if (rcsSystem.equip.armor.is_hold)
        {
            rcs_send_to_bleserver(ARMOR_CLASS, sync_ble_server_data, packlen);
        }
        free(sync_ble_server_data);
    }
    else
    {
        ESP_LOGE(GAME_TAG, "damage invalid");
    }
}

// 接收到命令以后，判断系统状态是否改变，如果改变了，就通知手机和从机
static void command_message_handle(uint8_t *buf, uint16_t buf_len)
{
    notify_mask_t notify_data_mask = 0;

    uint16_t data_head = 0;
    upload_data_type_t data_type;
    uint16_t data_len;
    while (data_head < buf_len)
    {
        data_len = buf[data_head];
        data_type = buf[data_head + 1];
        if (data_type == COMMAND_UPL)
        {
            uint8_t commnad_value = buf[data_head + 2];
            ESP_LOGE("", "command: %d", commnad_value);
            if (rcsSystem.net.is_connected)
            {
                ESP_LOGE(GAME_TAG, "command send to server");
                // send to server, request to start game
                size_t message_length = 0;
                uint32_t start_request[1] = {commnad_value};
                // ESP_LOGI(GAME_TAG, "send start game message to server");
                char *message = make_net_json_package(START_GAME_NET_UP, &message_length, &start_request);
                if (message)
                {
                    bc_send_package_to_server(message, message_length);
                    free(message);
                }
                // 服务器版本不直接开始游戏，只是告诉服务器，等待服务器命令开启游戏
            }
            else
            {
                take_mutex();
                switch (commnad_value)
                {
                case START_GAME_COMM:
                {
                    if (!rcsSystem.game.start)
                    {
                        ESP_LOGE(GAME_TAG, "no server, start game");
                        rcsSystem.game.start = true;
                        notify_data_mask |= (1 << GAME_START_NOTIFY);
                    }
                    else
                    {
                        ESP_LOGE(GAME_TAG, "already start game");
                    }
                    break;
                }
                case END_GAME_COMM:
                {
                    if (rcsSystem.game.start)
                    {
                        ESP_LOGE(GAME_TAG, "no server, end game");
                        if (reset_player())
                        {
                            notify_data_mask |= RESET_PLAYER_MASK;
                        }
                        notify_data_mask |= (1 << GAME_START_NOTIFY);
                        rcsSystem.game.start = false;
                    }
                    else
                    {
                        ESP_LOGE(GAME_TAG, "already end game");
                    }
                    break;
                }
                case INCREASE_DAMAGE_COMM:
                {
                    if (rcsSystem.game.player.living)
                    {
                        notify_data_mask |= change_player_damage(rcsSystem.game.player.damage + 30) << DAMAGE_NOTIFY;
                        // ESP_LOGE(GAME_TAG, "add damage: %d", rcsSystem.game.player.damage);
                    }
                    else
                    {
                        ESP_LOGE(GAME_TAG, "player dead, only can reborn");
                    }
                    break;
                }
                case DECREASE_DAMAGE_COMM:
                {
                    if (rcsSystem.game.player.living)
                    {
                        notify_data_mask |= change_player_damage(rcsSystem.game.player.damage - 30) << DAMAGE_NOTIFY;
                        // ESP_LOGE(GAME_TAG, "sub damage: %d", rcsSystem.game.player.damage);
                    }
                    else
                    {
                        ESP_LOGE(GAME_TAG, "player dead, only can reborn");
                    }
                    break;
                }
                case INCREASE_HP_COMM:
                {
                    if (rcsSystem.game.player.living)
                    {
                        if (change_player_hp(rcsSystem.game.player.hp + 50))
                        {
                            notify_data_mask |= (1 << HP_NOTIFY) | (1 << LIVING_NOTIFY);
                        }
                        // ESP_LOGE(GAME_TAG, "add hp: %d", rcsSystem.game.player.hp);
                    }
                    else
                    {
                        ESP_LOGE(GAME_TAG, "player dead, only can reborn");
                    }
                    break;
                }
                case DECREASE_HP_COMM:
                {
                    if (change_player_hp(rcsSystem.game.player.hp - 50))
                    {
                        notify_data_mask |= (1 << HP_NOTIFY) | (1 << LIVING_NOTIFY);
                        // ESP_LOGE(GAME_TAG, "sub hp: %d", rcsSystem.game.player.hp);
                    }
                    break;
                }
                case INCREASE_BULLET_COMM:
                {
                    if (rcsSystem.game.player.living)
                    {
                        notify_data_mask |= change_player_bullet_nums(rcsSystem.game.player.bullet_nums + 30) << BULLET_NUM_NOTIFY;
                        // ESP_LOGE(GAME_TAG, "add bullet: %d", rcsSystem.game.player.bullet_nums);
                    }
                    else
                    {
                        ESP_LOGE(GAME_TAG, "player dead, only can reborn");
                    }
                    break;
                }
                case KILL_COMM:
                {
                    if (rcsSystem.game.start)
                    {
                        if (change_player_hp(0))
                        {
                            notify_data_mask |= (1 << HP_NOTIFY) | (1 << LIVING_NOTIFY);
                        }
                        // ESP_LOGE(GAME_TAG, "kill, hp: %d", rcsSystem.game.player.hp);
                    }
                    else
                    {
                        ESP_LOGE(GAME_TAG, "game no start");
                    }
                    break;
                }
                case REBORN_COMM:
                {
                    if (rcsSystem.game.start && !rcsSystem.game.player.living)
                    {
                        if (change_player_hp(100))
                        {
                            notify_data_mask |= (1 << HP_NOTIFY) | (1 << LIVING_NOTIFY);
                        }
                        // ESP_LOGE(GAME_TAG, "reborn");
                    }
                    else
                    {
                        ESP_LOGE(GAME_TAG, "game no start or player is living");
                    }
                    break;
                }
                case SET_TEAM_A_COMM:
                {
                    notify_data_mask |= change_player_team(1) << TEAM_NOTIFY;
                    // ESP_LOGE(GAME_TAG, "set team A");
                    break;
                }
                case SET_TEAM_B_COMM:
                {
                    notify_data_mask |= change_player_team(2) << TEAM_NOTIFY;
                    // ESP_LOGE(GAME_TAG, "set team B");
                    break;
                }
                case RESET_ALL_COMM:
                {
                    if (reset_player())
                    {
                        notify_data_mask |= RESET_PLAYER_MASK;
                    }
                    // ESP_LOGE(GAME_TAG, "reset all");
                    break;
                }
                default:
                {
                    ESP_LOGE(GAME_TAG, "unknown remote command");
                    uint32_t param_arr[2] = {0};
                    param_arr[0] = REMOTE_UNKNOWN;
                    param_arr[1] = buf[data_head + 2];
                    size_t message_length;
                    char *message = make_net_json_package(ERROR_NET_UP, &message_length, param_arr);
                    if (message)
                    {
                        bc_send_package_to_server(message, message_length);
                        free(message);
                    }
                    break;
                }
                }
                give_mutex();
            }
        }
        else
        {
        }
        break;
        data_head += data_len;
    }
    if (notify_data_mask)
    {
        // //通知手机
        size_t message_length = 0;
        // ESP_LOGI("net", "send property changed message to server");
        char *message = make_net_json_package(PROPPERTY_CHANGE_NET_UP, &message_length, NULL);
        if (message)
        {
            bc_send_package_to_server(message, message_length);
            free(message);
        }
        // 通知所有从机
        uint16_t packlen;
        uint8_t *sync_ble_server_data = make_sync_data_package(&packlen, notify_data_mask);
        if (rcsSystem.equip.pri_weapon.screen.is_hold)
        {
            rcs_send_to_bleserver(GUN_SCREEN_CLASS, sync_ble_server_data, packlen);
        }
        if (rcsSystem.equip.pri_weapon.point.is_hold)
        {
            rcs_send_to_bleserver(GUN_POINT_CLASS, sync_ble_server_data, packlen);
        }
        if (rcsSystem.equip.armor.is_hold)
        {
            rcs_send_to_bleserver(ARMOR_CLASS, sync_ble_server_data, packlen);
        }
        free(sync_ble_server_data);
    }
}

/*
游戏线程任务：
处理来自自身，接收板，发射板的消息；
修改数据，
通知服务器，通知从机。
*/
void game_package_handle(uint8_t *buf)
{
    // 现在buf首地址指向的是整包长度数据块
    uint16_t data_head = 0;
    uint16_t data_len = buf[data_head];
    upload_data_type_t data_type = buf[data_head + 1];
    if (data_type != PACKAGE_LEN_UPL)
    { // 校验一下
        ESP_LOGE(GAME_TAG, "package_len error");
        return;
    }
    uint8_t package_len = buf[data_head + 2];
    package_len -= data_len;
    buf += data_len;
    // 现在buf首地址指向的是 消息类型和电量 数据块
    data_len = buf[data_head];
    data_type = buf[data_head + 1];
    if (data_type != MESSAGE_TYPE_BATTERY_UPL)
    { // 校验一下
        ESP_LOGE(GAME_TAG, "is not message type error");
        return;
    }
    message_type_t mess_type = buf[data_head + 2];
    int8_t batt = buf[data_head + 3];
    package_len -= data_len;
    buf += data_len;

    // 先处理一次电量
    switch (mess_type)
    {
    case GUN_POINT_VER_TYPE:
    case GUN_POINT_BATTERY_TYPE:
    case GUN_POINT_FIRE_TYPE:
    case GUN_POINT_COMMAND_TYPE:
    case GUN_POINT_CLOSE_CONNECT_TYPE:
        rcsSystem.equip.pri_weapon.point.batt = batt;
        ESP_LOGI(GAME_TAG, "GUN_POINT batt:%d", rcsSystem.equip.pri_weapon.point.batt);
        break;
    case GUN_SCREEN_VER_TYPE:
    case GUN_SCREEN_BATTERY_TYPE:
    case GUN_SCREEN_COMMAND_TYPE:
    case GUN_SCREEN_CLOSE_CONNECT_TYPE:
        rcsSystem.equip.pri_weapon.screen.batt = batt;
        ESP_LOGI(GAME_TAG, "GUN_SCREEN batt:%d", rcsSystem.equip.pri_weapon.screen.batt);
        break;
    case ARMOR_VER_TYPE:
    case ARMOR_BATTERY_TYPE:
    case ARMOR_COMMAND_TYPE:
    case ARMOR_BE_HITTED_TYPE:
    case ARMOR_CLOSE_CONNECT_TYPE:
        rcsSystem.equip.armor.batt = batt;
        ESP_LOGI(GAME_TAG, "ARMOR batt:%d", rcsSystem.equip.armor.batt);
        break;
    default:
        break;
    }

    // 现在buf首地址指向的是包具体数据内容
    switch (mess_type)
    {
    case GUN_POINT_VER_TYPE:
    {
        uint16_t packagelen = 0;
        ESP_LOGI(GAME_TAG, "GUN_POINT_VER_TYPE");
        // 初次连接，需要把控制板属性同步过去
        notify_mask_t notify_data_mask = POINT_INIT_MASK; // 0000 0001 1111 1110
        uint8_t *package = make_sync_data_package(&packagelen, notify_data_mask);
        rcs_send_to_bleserver(GUN_POINT_CLASS, package, packagelen);
        free(package);
        if (rcsSystem.equip.pri_weapon.screen.is_hold)
        { // 更新屏幕数字和电量
            notify_mask_t mask = (1 << CONN_CLI_NUM_NOTIFY) | (1 << SCRBATT_NOTIFY);
            package = make_sync_data_package(&packagelen, mask);
            rcs_send_to_bleserver(GUN_SCREEN_CLASS, package, packagelen);
            free(package);
        }
        // set_allow_open(true);
        break;
    }
    case GUN_SCREEN_VER_TYPE:
    {
        uint16_t packagelen = 0;
        ESP_LOGI(GAME_TAG, "GUN_SCREEN_VER_TYPE");
        // 初次连接，需要把控制板属性同步过去
        notify_mask_t notify_data_mask = SCREEN_INIT_MASK; //
        uint8_t *package = make_sync_data_package(&packagelen, notify_data_mask);
        ESP_LOGE(GAME_TAG, "send GUN_SCREEN init package. len:%d", packagelen);
        rcs_send_to_bleserver(GUN_SCREEN_CLASS, package, packagelen);
        free(package);
        // set_allow_open(true);
        break;
    }
    case ARMOR_VER_TYPE:
    {
        uint16_t packagelen = 0;
        ESP_LOGI(GAME_TAG, "ARMOR_VER_TYPE");
        // 初次连接，需要把控制板属性同步过去
        notify_mask_t notify_data_mask = ARMOR_INIT_MASK;
        uint8_t *package = make_sync_data_package(&packagelen, notify_data_mask);
        rcs_send_to_bleserver(ARMOR_CLASS, package, packagelen);
        free(package);
        if (rcsSystem.equip.pri_weapon.screen.is_hold)
        { // 更新屏幕数字
            notify_mask_t mask = (1 << CONN_CLI_NUM_NOTIFY) | (1 << SCRBATT_NOTIFY);
            package = make_sync_data_package(&packagelen, mask);
            rcs_send_to_bleserver(GUN_SCREEN_CLASS, package, packagelen);
            free(package);
        }
        // set_allow_open(true);
        break;
    }
    case GUN_POINT_FIRE_TYPE:
    {
        // ESP_LOGI(GAME_TAG, "GUN_POINT_FIRE_TYPE");
        fire_message_handle(buf, package_len);
        break;
    }
    case GUN_POINT_COMMAND_TYPE:
    case GUN_SCREEN_COMMAND_TYPE:
    case ARMOR_COMMAND_TYPE:
    case CORE_ARMOR_COMMAND_TYPE:
    {
        // ESP_LOGE(GAME_TAG, "COMMAND_TYPE");
        command_message_handle(buf, package_len);
        break;
    }
    case ARMOR_BE_HITTED_TYPE:
    case CORE_ARMOR_BE_HITTED_TYPE:
    {
        // ESP_LOGI(GAME_TAG, "BE_HITTED_TYPE");
        be_hitted_message_handle(buf, package_len);
        break;
    }
    case GUN_POINT_CLOSE_CONNECT_TYPE:
    {
        // ESP_LOGI(GAME_TAG, "GUN_POINT_CLOSE_CONNECT_TYPE");
        rcsSystem.equip.pri_weapon.point.is_hold = false;
        rcs_close_all_connection(GUN_POINT_CLASS);
        clean_remote_bda(GUN_POINT_CLASS);
        break;
    }
    case GUN_SCREEN_CLOSE_CONNECT_TYPE:
    {
        ESP_LOGI(GAME_TAG, "GUN_SCREEN_CLOSE_CONNECT_TYPE");
        rcsSystem.equip.pri_weapon.screen.is_hold = false;
        rcs_close_all_connection(GUN_SCREEN_CLASS);
        clean_remote_bda(GUN_SCREEN_CLASS);
        break;
    }
    case ARMOR_CLOSE_CONNECT_TYPE:
    {
        // ESP_LOGI(GAME_TAG, "ARMOR_CLOSE_CONNECT_TYPE");
        rcsSystem.equip.pri_weapon.point.is_hold = false;
        rcs_close_all_connection(ARMOR_CLASS);
        clean_remote_bda(ARMOR_CLASS);
        break;
    }
    default:
        break;
    }
}

/*
可以优化消息的长度

*/

uint8_t package_len_table[DATA_NOTIFY_CLASS_NUM] = {
    PACKAGE_LEN_NOTI_LEN,
    PLAYER_ID_NOTI_LEN,
    LIVING_NOTI_LEN,
    GAME_START_NOTI_LEN,

    TEAM_NOTI_LEN,
    ROOM_NOTI_LEN,
    HP_NOTI_LEN,
    DAMAGE_NOTI_LEN,

    BULLET_NUM_NOTI_LEN,
    CONN_CLI_NUM_NOTI_LEN,
    LOW_WARNING_NOTI_LEN,
    AUDIO_SN_NOTI_LEN,

    DEATH_NOTI_LEN,
    KILL_NOTI_LEN,
    TEAM_SCORE_NOTI_LEN,
    CLOSE_CONNECT_NOTI_LEN,

    POWER_OFF_NOTI_LEN,
    BE_HITTED_NOTI_LEN,
    SCRHINT_NOTI_LEN,
    SCRBATT_NOTI_LEN,

    NETSTAT_NOTI_LEN};

uint8_t *make_play_music_package(uint16_t *packlen, uint8_t music_sn)
{
    *packlen = 0;
    uint8_t len = PACKAGE_LEN_NOTI_LEN + AUDIO_SN_NOTI_LEN;
    uint8_t *package = calloc(1, len);
    if (package == NULL)
    {
        return package;
    }
    uint8_t data_head = 0;
    uint8_t data_len;

    data_len = PACKAGE_LEN_NOTI_LEN;
    package[data_head] = data_len;
    package[data_head + 1] = PACKAGE_LEN_NOTIFY;
    package[data_head + 2] = len;
    data_head += data_len;

    data_len = AUDIO_SN_NOTI_LEN;
    package[data_head] = data_len;
    package[data_head + 1] = AUDIO_SN_NOTIFY;
    package[data_head + 2] = music_sn;
    data_head += data_len;
    if (data_head != len)
    {
        ESP_LOGE(SYS_TAG, "physical data len: %d, target len :%d, error", data_head, len);
    }
    *packlen = data_head;
    // esp_log_buffer_hex(SYS_TAG, package, data_head);
    return package;
}

uint8_t *make_hint_data_package(uint16_t *packlen, char *hint_data)
{
    *packlen = 0;
    uint8_t len = PACKAGE_LEN_NOTI_LEN + SCRHINT_NOTI_LEN;
    uint8_t *package = calloc(1, len);
    if (package == NULL)
    {
        return package;
    }
    uint8_t data_head = 0;
    uint8_t data_len;

    data_len = PACKAGE_LEN_NOTI_LEN;
    package[data_head] = data_len;
    package[data_head + 1] = PACKAGE_LEN_NOTIFY;
    package[data_head + 2] = len;
    data_head += data_len;

    data_len = SCRHINT_NOTI_LEN;
    package[data_head] = data_len;
    package[data_head + 1] = SCRHINT_NOTIFY;
    int i = 0;
    for (i = 0; i < 7; ++i)
    {
        package[data_head + 2 + i] = hint_data[i];
    }
    data_head += data_len;
    if (data_head != len)
    {
        ESP_LOGE(SYS_TAG, "physical data len: %d, target len :%d, error", data_head, len);
    }
    *packlen = data_head;
    // esp_log_buffer_hex(SYS_TAG, package, data_head);
    return package;
}

uint8_t *make_sync_data_package(uint16_t *packlen, notify_mask_t mask)
{
    *packlen = 0;
    uint8_t len = PACKAGE_LEN_NOTI_LEN;
    for (size_t i = PLAYER_ID_NOTIFY; i < DATA_NOTIFY_CLASS_NUM; i++)
    {
        if ((mask & (1 << i)) != 0)
        {
            len += package_len_table[i];
        }
    }
    // ESP_LOGE("sync slave package", "mask: %d, len: %d.", mask, len);
    uint8_t *package = calloc(1, len);
    if (package == NULL)
    {
        return package;
    }
    uint8_t data_head = 0;
    uint8_t data_len;

    data_len = PACKAGE_LEN_NOTI_LEN;
    package[data_head] = data_len;
    package[data_head + 1] = PACKAGE_LEN_NOTIFY;
    package[data_head + 2] = len;
    data_head += data_len;
    if (mask & (1 << PLAYER_ID_NOTIFY))
    {
        data_len = PLAYER_ID_NOTI_LEN;
        package[data_head] = data_len;
        package[data_head + 1] = PLAYER_ID_NOTIFY;
        package[data_head + 2] = (rcsSystem.game.player.id & 0x00FF0000) >> 16;
        package[data_head + 3] = (rcsSystem.game.player.id & 0x0000FF00) >> 8;
        package[data_head + 4] = (rcsSystem.game.player.id & 0x000000FF);
        data_head += data_len;
    }
    if (mask & (1 << LIVING_NOTIFY))
    {
        data_len = LIVING_NOTI_LEN;
        package[data_head] = data_len;
        package[data_head + 1] = LIVING_NOTIFY;
        package[data_head + 2] = rcsSystem.game.player.living;
        data_head += data_len;
    }
    if (mask & (1 << GAME_START_NOTIFY))
    {
        data_len = GAME_START_NOTI_LEN;
        package[data_head] = data_len;
        package[data_head + 1] = GAME_START_NOTIFY;
        package[data_head + 2] = rcsSystem.game.start;
        data_head += data_len;
    }
    if (mask & (1 << TEAM_NOTIFY))
    {
        data_len = TEAM_NOTI_LEN;
        package[data_head] = data_len;
        package[data_head + 1] = TEAM_NOTIFY;
        package[data_head + 2] = rcsSystem.game.player.team;
        data_head += data_len;
    }
    if (mask & (1 << ROOM_NOTIFY))
    {
        data_len = ROOM_NOTI_LEN;
        package[data_head] = data_len;
        package[data_head + 1] = ROOM_NOTIFY;
        package[data_head + 2] = rcsSystem.game.player.room;
        data_head += data_len;
    }
    if (mask & (1 << HP_NOTIFY))
    {
        data_len = HP_NOTI_LEN;
        package[data_head] = data_len;
        package[data_head + 1] = HP_NOTIFY;
        package[data_head + 2] = (rcsSystem.game.player.hp & 0xFF00) >> 8;
        package[data_head + 3] = (rcsSystem.game.player.hp & 0x00FF);
        data_head += data_len;
    }
    if (mask & (1 << DAMAGE_NOTIFY))
    {
        data_len = DAMAGE_NOTI_LEN;
        package[data_head] = data_len;
        package[data_head + 1] = DAMAGE_NOTIFY;
        package[data_head + 2] = (rcsSystem.game.player.damage & 0xFF00) >> 8;
        package[data_head + 3] = (rcsSystem.game.player.damage & 0x00FF);
        data_head += data_len;
    }
    if (mask & (1 << BULLET_NUM_NOTIFY))
    {
        data_len = BULLET_NUM_NOTI_LEN;
        package[data_head] = data_len;
        package[data_head + 1] = BULLET_NUM_NOTIFY;
        package[data_head + 2] = (rcsSystem.game.player.bullet_nums & 0xFF00) >> 8;
        package[data_head + 3] = (rcsSystem.game.player.bullet_nums & 0x00FF);
        data_head += data_len;
    }
    if (mask & (1 << CONN_CLI_NUM_NOTIFY))
    {
        data_len = CONN_CLI_NUM_NOTI_LEN;
        package[data_head] = data_len;
        package[data_head + 1] = CONN_CLI_NUM_NOTIFY;
        package[data_head + 2] = rcsSystem.equip.conn_num;
        data_head += data_len;
    }
    if (mask & (1 << LOW_WARNING_NOTIFY))
    {
        data_len = LOW_WARNING_NOTI_LEN;
        package[data_head] = data_len;
        package[data_head + 1] = LOW_WARNING_NOTIFY;
        package[data_head + 2] = rcsSystem.equip.low_batt_alert;
        data_head += data_len;
    }
    if (mask & (1 << DEATH_NOTIFY))
    {
        data_len = DEATH_NOTI_LEN;
        package[data_head] = data_len;
        package[data_head + 1] = DEATH_NOTIFY;
        package[data_head + 2] = rcsSystem.game.player.death;
        data_head += data_len;
    }
    if (mask & (1 << KILL_NOTIFY))
    {
        data_len = KILL_NOTI_LEN;
        package[data_head] = data_len;
        package[data_head + 1] = KILL_NOTIFY;
        package[data_head + 2] = rcsSystem.game.player.kill;
        data_head += data_len;
    }
    if (mask & (1 << TEAM_SCORE_NOTIFY))
    {
        data_len = TEAM_SCORE_NOTI_LEN;
        package[data_head] = data_len;
        package[data_head + 1] = TEAM_SCORE_NOTIFY;
        package[data_head + 2] = (rcsSystem.game.player.team_score & 0xFF00) >> 8;
        package[data_head + 3] = (rcsSystem.game.player.team_score & 0x00FF);
        data_head += data_len;
    }
    if (mask & (1 << CLOSE_CONNECT_NOTIFY))
    {
        data_len = CLOSE_CONNECT_NOTI_LEN;
        package[data_head] = data_len;
        package[data_head + 1] = CLOSE_CONNECT_NOTIFY;
        data_head += data_len;
    }
    if (mask & (1 << POWER_OFF_NOTIFY))
    {
        data_len = POWER_OFF_NOTI_LEN;
        package[data_head] = data_len;
        package[data_head + 1] = POWER_OFF_NOTIFY;
        data_head += data_len;
    }
    if (mask & (1 << BE_HITTED_NOTIFY))
    {
        data_len = BE_HITTED_NOTI_LEN;
        package[data_head] = data_len;
        package[data_head + 1] = BE_HITTED_NOTIFY;
        data_head += data_len;
    }
    if (mask & (1 << SCRBATT_NOTIFY))
    {
        data_len = SCRBATT_NOTI_LEN;
        package[data_head] = data_len;
        package[data_head + 1] = SCRBATT_NOTIFY;
        package[data_head + 2] = (uint8_t)rcsSystem.equip.core.battery;
        package[data_head + 3] = (uint8_t)rcsSystem.equip.pri_weapon.point.batt;
        package[data_head + 4] = (uint8_t)rcsSystem.equip.pri_weapon.screen.batt;
        package[data_head + 5] = (uint8_t)rcsSystem.equip.armor.batt;
        data_head += data_len;
    }
    if (mask & (1 << NET_STATE_NOTIFY))
    {
        data_len = NETSTAT_NOTI_LEN;
        package[data_head] = data_len;
        package[data_head + 1] = NET_STATE_NOTIFY;
        package[data_head + 2] = rcsSystem.net.is_connected;
        data_head += data_len;
    }
    if (data_head != len)
    {
        ESP_LOGE(SYS_TAG, "physical data len: %d, target len :%d, error", data_head, len);
    }
    *packlen = data_head;
    // esp_log_buffer_hex(SYS_TAG, package, data_head);
    return package;
}
